package com.ufgov.smartclient.component.table;

import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.GraphicsEnvironment;
import java.awt.HeadlessException;
import java.awt.LayoutManager;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.datatransfer.Transferable;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.print.Printable;
import java.awt.print.PrinterException;
import java.awt.print.PrinterJob;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.Externalizable;
import java.io.File;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.math.BigDecimal;
import java.text.MessageFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Enumeration;
import java.util.EventObject;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Locale;
import java.util.Map;
import java.util.Set;

import javax.print.PrintService;
import javax.print.attribute.HashPrintRequestAttributeSet;
import javax.print.attribute.PrintRequestAttributeSet;
import javax.swing.AbstractAction;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JPopupMenu;
import javax.swing.JProgressBar;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.JViewport;
import javax.swing.ScrollPaneConstants;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.TransferHandler;
import javax.swing.UIManager;
import javax.swing.event.PopupMenuEvent;
import javax.swing.event.PopupMenuListener;
import javax.swing.plaf.UIResource;
import javax.swing.table.JTableHeader;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;
import javax.swing.table.TableModel;

import jxl.CellView;
import jxl.Workbook;
import jxl.WorkbookSettings;
import jxl.format.Alignment;
import jxl.format.PageOrientation;
import jxl.write.Label;
import jxl.write.WritableCell;
import jxl.write.WritableCellFormat;
import jxl.write.WritableFont;
import jxl.write.WritableSheet;
import jxl.write.WritableWorkbook;
import jxl.write.WriteException;
import jxl.write.biff.RowsExceededException;
import sun.swing.PrintingStatus;

/**
 * <p>Title: com.ufgov.smartclient.component</p>
 * <p>Description: JTable的扩展，更改TableHeader为JGroupableTableHeader，以支持表头分组
 * 并增加列显示/隐藏，列宽自动保存，等功能</p>
 * <p>Copyright: Copyright 2010 ufgov, Inc.</p>
 * <p>Company: ufgov</p>
 * <p>创建时间: 2007-7-10</p>
 * @author 刘永伟(manlge)
 * @version 1.5
 */
public class JGroupableTable extends JTable {

  private static final String uiClassID = "GroupableTableUI";

  /**
   * 默认的行间隔颜色
   */
  private static final Color DEFAULT_ALTERNATING_COLOR = new Color(0xF1EEFC);

  private static final long serialVersionUID = 700050214922179080L;

  /**
   * <p>Title: com.ufgov.smartclient.component</p>
   * <p>Description: 导出Excel时Sheet的方向 </p>
   * <p>Copyright: Copyright 2010 ufgov, Inc.</p>
   * <p>Company: ufgov</p>
   * <p>创建时间: Sep 26, 2010</p>
   * @author 刘永伟(manlge)
   * @version 1.0
   */
  public static enum SheetOrientation {
    /**
     * 纵向
     */
    VERTICAL,

    /**
     * 横向
     */
    HORIZONTAL;
  }

  /**
   * <p>Title: com.ufgov.smartclient.component</p>
   * <p>Description: 行对象，用来表过一个行</p>
   * <p>Copyright: Copyright 2010 ufgov, Inc.</p>
   * <p>Company: ufgov</p>
   * <p>创建时间: 2010-3-25</p>
   * @author 刘永伟(manlge)
   * @version 1.0
   */
  protected static class Row {

    /**
     * 行索引
     */
    private int index = -1;

    /**
     * 用来获取当前行索引号
     * @return 当前行索引
     */
    public int getIndex() {
      return index;
    }

    /**
     * 设置索引
     * @param index
     * @see #getIndex()
     * @see #inc()
     * @see #dec()
     */
    public void setIndex(int index) {
      this.index = index;
    }

    /**
     * 递增
     * @see #setIndex(int)
     * @see #dec()
     */
    public void inc() {
      index++;
    }

    /**
     * 递减
     * @see #inc()
     * @see #dec()
     * @see #setIndex(int)
     */
    public void dec() {
      index--;
    }

    /**
     * 创建<code>Row</code>实例,index初始化为0
     */
    public Row() {
      this(0);
    }

    /**
     * 根据index创建<code>Row</code>实例
     * @param index 初始行号
     */
    public Row(int index) {
      super();
      this.index = index;
    }

  }

  /**
   * <p>Title: com.ufgov.smartclient.component</p>
   * <p>Description: 外观装饰单元格渲染器</p>
   * <p>Copyright: Copyright 2010 ufgov, Inc.</p>
   * <p>Company: ufgov</p>
   * <p>创建时间: 2010-3-25</p>
   * @author 刘永伟(manlge)
   * @version 1.0
   */
  private final class AppearanceDectoratorCellRenderer implements TableCellRenderer,
    TableCellDisplayValueProvider {

    /**
     * 原始的renderer
     */
    private TableCellRenderer delegate;

    /**
     * 根据delegate创建实例，
     * @param delegate 原始的renderer，用于功能委派
     */
    private AppearanceDectoratorCellRenderer(TableCellRenderer delegate) {
      this.delegate = delegate;
    }

    public Component getTableCellRendererComponent(JTable table, Object value,
      boolean isSelected, boolean hasFocus, int row, int column) {
      //      if (!(value instanceof Boolean)) { TODO: 不再进行转换
      //        value = getDisplayValueAt(row, column); //除value的显示表现形式
      //      }
      Component comp = delegate.getTableCellRendererComponent(table, value,
        isSelected, hasFocus, row, column);
      //根据useCellRendererColor设置前景/背景色
      if (!useCellRendererColor && !isSelected) {
        comp.setBackground(getCellBackground(row, column));
        comp.setForeground(getCellForeground(row, column));
      }
      return comp;
    }

    @Override
    public Object getDisplayValue(JTable table, Object value, int row, int column) {
      if (delegate instanceof TableCellDisplayValueProvider) {
        return ((TableCellDisplayValueProvider) delegate).getDisplayValue(table,
          value, row, column);
      }
      return value;
    }
  }

  /**
   * 行表头，用于显示行号和CheckBox列
   * {@link JGroupableTable#getTableRowHeader()}
   */
  protected TableRowHeader tableRowHeader = null;

  /**
   * 是否使用useCellRendererColor的颜色，包括前景色和背景色
   */
  private boolean useCellRendererColor = false;

  /**
   * 合计行Model
   */
  private TableTotalLineModel totalLineModel;

  /**
   * 合计行的高度
   * @see #setTotalLineCellRenderer(TableTotalLineCellRenderer)
   * @see #getTotalLineCellRenderer()
   * @see #setTotalLineModel(TableTotalLineModel)
   * @see #getTotalLineModel()
   */
  private int totalLineHeight = 18;

  /**
   * 合计行渲染器
   */
  private TableTotalLineCellRenderer totalLineCellRenderer;

  /**
   * 是否只读.
   */
  private boolean readOnly = false;

  /**
   * 是否即时编辑.
   * @see #isInstantEdit()
   * @see #setInstantEdit(boolean)
   */
  private boolean instantEdit = false;

  /**
   * 保存配置时使用的key.
   * @see #setPreferencesKey(String)
   * @see #getPreferencesKey()
   */
  private String preferencesKey;

  /**
   * 表格所在的ScrollPane.
   */
  protected JScrollPane ownerScrollPane = null;

  /**
   * 所有列用于右键菜单中显示/隐藏指定列.
   */

  protected Map<JCheckBox, TableColumn> checkBoxColumnMap;

  /**
   * 表格配置加载保存 对象，可以通过自定义PreferenceStore实现远程存储
   * 默认使用 {@link DefaultPreferenceStore}，配置信息将保存到注册表中(windows)
   * 或文件系统中(linux/unix)
   * @see JGroupableTable#setPreferenceStore(PreferenceStore)
   * @see JGroupableTable#getPreferenceStore()
   */
  private PreferenceStore preferenceStore;

  /**
   * 行间隔/交替色，注，{@link #isUseCellRendererColor()}为false无效
   * @see #setUseCellRendererColor(boolean)
   * @see #isUseCellRendererColor()
   * @see #setAlternatingColor(Color)
   */
  private Color alternatingColor;

  /**
   * 显示/隐藏字段的菜单.
   * @see JGroupableTable#showPopupMenu(Component, int, int)
   * @see #updatePopupMenu()
   * @see #updatePopupMenuComponentEnable()
   */
  protected JPopupMenu popupMenu;

  /**
   * 导出到excel时的方向，默认为纵向
   */
  private SheetOrientation sheetOrientation = SheetOrientation.VERTICAL;

  /**
   * 导出到excel时单元格格式提供器
   */
  private CellFormatPrivoder cellFormatPrivoder = new DefaultCellFormatPrivoder();

  /**
   * 是否显示右键表头菜单
   */
  private boolean showTableHeaderPopupMenu = true;

  /**
   * 设置是否显示表头的右键菜单
   * @param showTableHeaderPopupMenu true显示，false不显示
   */
  public void setShowTableHeaderPopupMenu(boolean showTableHeaderPopupMenu) {
    this.showTableHeaderPopupMenu = showTableHeaderPopupMenu;
  }

  /**
   * @return 是否显示表头右键单菜,显示返加true，不显示返回false
   */
  public boolean isShowTableHeaderPopupMenu() {
    return showTableHeaderPopupMenu;
  }

  /**
   * 设置导出到excel时单元格格式提供器
   * @param cellFormatPrivoder
   */
  public void setCellFormatPrivoder(CellFormatPrivoder cellFormatPrivoder) {
    if (cellFormatPrivoder == null) {
      throw new IllegalArgumentException("cellFormatPrivoder can't not be null");
    }
    this.cellFormatPrivoder = cellFormatPrivoder;
  }

  /**
   * @return 导出到excel时单元格格式提供器
   */
  public CellFormatPrivoder getCellFormatPrivoder() {
    return cellFormatPrivoder;
  }

  /**
   * 设置导出Excel时的Sheet方向，
   * 当导出数据到Excel时，可能需要控制导出的纸张方向，以便于打印，减少工手设置，请参见：<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {@link SheetOrientation#VERTICAL}，{@link SheetOrientation#HORIZONTAL}
   * @param sheetOrientation
   * @see #getSheetOrientation()
   */
  public void setSheetOrientation(SheetOrientation sheetOrientation) {
    this.sheetOrientation = sheetOrientation;
  }

  /**
   * @return 导出到excel时的Sheet方向，{@link SheetOrientation#VERTICAL}，{@link SheetOrientation#HORIZONTAL}
   * @see #getSheetOrientation()
   */
  public SheetOrientation getSheetOrientation() {
    return sheetOrientation;
  }

  /**
   * 设置合计行model，为合计行提供数据
   * @param totalLineModel
   * @see #getTotalLineModel()
   */
  public void setTotalLineModel(TableTotalLineModel totalLineModel) {
    TableTotalLineModel old = this.totalLineModel;
    this.totalLineModel = totalLineModel;
    firePropertyChange("totalLineModel", old, totalLineModel);
  }

  /**
   * 设置合计行的单元格渲染器，该方法在 {@link #getTotalLineModel()} == null 时没有意义
   * @param totalLineCellRenderer
   * @see #totalLineCellRenderer
   * @see #setTotalLineCellRenderer(TableTotalLineCellRenderer)
   */
  public void setTotalLineCellRenderer(
    TableTotalLineCellRenderer totalLineCellRenderer) {
    this.totalLineCellRenderer = totalLineCellRenderer;
  }

  /**
   * @return 合计行的单元格渲染器
   * @see #setTotalLineCellRenderer(TableTotalLineCellRenderer)
   * @see #totalLineCellRenderer
   */
  public TableTotalLineCellRenderer getTotalLineCellRenderer() {
    if (totalLineCellRenderer == null) {
      totalLineCellRenderer = createDefaultTotalLineCellRenderer();
    }
    return totalLineCellRenderer;
  }

  /**
   * @return 默认的合计行表格渲染器
   * @see #totalLineCellRenderer
   * @see #setTotalLineCellRenderer(TableTotalLineCellRenderer)
   * @see #getTotalLineCellRenderer()
   */
  protected TableTotalLineCellRenderer createDefaultTotalLineCellRenderer() {
    return new DefaultTableTotalLineCellRenderer();

  }

  /**
   * @return 计行的model
   * @see #setTotalLineModel(TableTotalLineModel)
   */
  public TableTotalLineModel getTotalLineModel() {
    return totalLineModel;
  }

  /**
   * 设置合计行的行高
   * @param totalLineHeight
   */
  public void setTotalLineHeight(int totalLineHeight) {
    if (totalLineHeight < 1) {
      throw new IllegalArgumentException("无效的totalLineHeight，必须>0");
    }
    int old = this.totalLineHeight;
    this.totalLineHeight = totalLineHeight;
    firePropertyChange("totalLineHeight", old, totalLineHeight);
  }

  public int getTotalLineHeight() {
    return totalLineHeight;
  }

  /**
   * 设置表格行的交替色
   * @param alternatingColor
   * @see #getAlternatingColor()
   * @see #DEFAULT_ALTERNATING_COLOR
   */
  public void setAlternatingColor(Color c) {
    this.alternatingColor = c;
  }

  /**
   * 设置是否使用CellRenderer的颜色，注：如果为true，{@link #setAlternatingColor(Color)} ，
   * {@link #getCellBackground(int, int)}，{@link #getCellForeground(int, int)}将失效
   * @param useCellRendererColor
   * @see #setAlternatingColor(Color)
   * @see JGroupableTable#getCellBackground(int, int)
   * @see JGroupableTable#getCellForeground(int, int)
   */
  public void setUseCellRendererColor(boolean useCellRendererColor) {
    boolean old = this.useCellRendererColor;
    this.useCellRendererColor = useCellRendererColor;
    firePropertyChange("useCellRendererColor", old, useCellRendererColor);
  }

  /**
   * 返回是否使用CellRenderer的背景色，默认false，如果为true，setAlternatingColor(Color)，
   * getCellBackground(int, int)，getCellForeground(int, int)将失效
   * @return true 使用CellRenderer的背景色，false不使 用CellRenderer的背景色
   * @see JGroupableTable#getCellBackground(int, int)
   * @see JGroupableTable#getCellForeground(int, int)
   * @see JGroupableTable#getAlternatingColor()
   */
  public boolean isUseCellRendererColor() {
    return useCellRendererColor;
  }

  /**
   * 返回表格行的交替色，注：在{@link #isUseCellRendererColor()}返回true时无效
   * @return 行交替显示的背景色
   * @see JGroupableTable#isUseCellRendererColor()
   * @see JGroupableTable#setUseCellRendererColor(boolean)
   */
  public Color getAlternatingColor() {
    if (alternatingColor == null) {
      alternatingColor = DEFAULT_ALTERNATING_COLOR;
    }
    return alternatingColor;
  }

  /**
   * @return 保存配置参数的key
   * @see #setPreferencesKey(String)
   * @see #getPreferenceStore()
   * @see #setPreferenceStore(PreferenceStore)
   */
  public String getPreferencesKey() {
    return preferencesKey;
  }

  /**
   * 设置保存配置参数的key，如果不设置，配置参数将不会自动保存和加载，
   * 注，需要{@link #setModel(TableModel)}.
   * @param preferencesKey
   * @see #getPreferencesKey()
   * @see #getPreferenceStore()
   * @see #setPreferenceStore(PreferenceStore)
   */
  public void setPreferencesKey(String preferencesKey) {
    debug("preferencesKey: " + preferencesKey);
    this.preferencesKey = preferencesKey;
  }

  /**
   * @return 表格行表头.
   * @see JGroupableTable#createDefaultTableRowHeader()
   */
  public TableRowHeader getTableRowHeader() {
    if (tableRowHeader == null) {
      tableRowHeader = createDefaultTableRowHeader();
    }
    return tableRowHeader;
  }

  /**
   * @return 创建的默认TableRowHeader，子类可覆盖该方法，创建自定义的实列.
   * @see JGroupableTable#getTableRowHeader()
   */
  protected TableRowHeader createDefaultTableRowHeader() {
    TableRowHeader tableRowHeader = new TableRowHeader(this, new RowHeaderRenderer(
      this));
    return tableRowHeader;
  }

  /**
   * 设置PreferenceStore，用于配置信息的保存和加载.
   * @param preferenceStore
   * @see JGroupableTable#getPreferenceStore()
   */
  public void setPreferenceStore(PreferenceStore preferenceStore) {
    if (preferenceStore == this.preferenceStore)
      return;
    PreferenceStore old = this.preferenceStore;
    this.preferenceStore = preferenceStore;
    firePropertyChange("preferenceStore", old, preferenceStore);
  }

  /**
   * @return PreferenceStore，用于保存和加载表格配置.
   * @see JGroupableTable#setPreferenceStore(PreferenceStore)
   */
  public PreferenceStore getPreferenceStore() {
    if (preferenceStore == null) {
      preferenceStore = createDefaultPreferenceStore();
    }
    return preferenceStore;
  }

  /**
   * 创建默认的PreferenceStore，子类可实现该方法更改默认实例，如实现网络存储.
   * @return 默认的PreferenceStore
   * @see JGroupableTable#getPreferenceStore()
   * @see JGroupableTable#setPreferenceStore(PreferenceStore)
   * @see #getPreferencesKey()
   * @see #setPreferencesKey(String)
   */
  protected PreferenceStore createDefaultPreferenceStore() {
    return new DefaultPreferenceStore();
  }

  @Override
  protected void configureEnclosingScrollPane() {
    Container p = getParent();
    if (p instanceof JViewport) {
      Container gp = p.getParent();
      if (gp instanceof JScrollPane) {
        JScrollPane scrollPane = (JScrollPane) gp;
        ownerScrollPane = scrollPane;
        JViewport viewport = scrollPane.getViewport();
        if (viewport == null || viewport.getView() != this) {
          return;
        }
        scrollPane.setColumnHeaderView(getTableHeader());

        if (scrollPane != null) {
          if (getModel() != null && getModel().getRowCount() > 0) {
            /**安装tableRowHeader*/
            scrollPane.setRowHeaderView(tableRowHeader);
            //安装角组件
            scrollPane.setCorner(ScrollPaneConstants.UPPER_LEFT_CORNER,
              getTableRowHeader().getRowHeaderUpperLeftComponent());
          } else {
            scrollPane.setRowHeaderView(null);
            scrollPane.setCorner(ScrollPaneConstants.UPPER_LEFT_CORNER, null);
          }
          //          scrollPane
          //            .setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
          //          scrollPane
          //            .setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);
        }
        javax.swing.border.Border border = scrollPane.getBorder();
        if (border == null || border instanceof UIResource) {
          javax.swing.border.Border scrollPaneBorder = UIManager
            .getBorder("Table.scrollPaneBorder");
          if (scrollPaneBorder != null) {
            scrollPane.setBorder(scrollPaneBorder);
          }
        }
      } else {
        ownerScrollPane = null;
      }
    }
    installTableRowHeader();
  }

  /**
   * 在Table中执行回车后执行移动到下一个单元格动作.
   */
  private static KeyListener enter2Table = new KeyAdapter() {
    @Override
    public void keyPressed(KeyEvent e) {
      if (e.getSource() instanceof JComboBox) {
        JComboBox comboBox = (JComboBox) e.getSource();
        if (e.getKeyCode() == KeyEvent.VK_ENTER && !comboBox.isPopupVisible()) {
          e.setKeyCode(KeyEvent.VK_TAB);
        }
      } else {
        if (e.getKeyCode() == KeyEvent.VK_ENTER) {
          e.setKeyCode(KeyEvent.VK_TAB);
        }
      }
    }
  };

  @Override
  public void changeSelection(int rowIndex, int columnIndex, boolean toggle,
    boolean extend) {
    super.changeSelection(rowIndex, columnIndex, toggle, extend);
    if (this.getSelectedColumnCount() > 1 || this.getSelectedRowCount() > 1)
      return;
    //进入编辑状态（模拟F2按下）
    if (instantEdit) {
      SwingUtilities.invokeLater(new Runnable() {
        public void run() {
          //
          //        KeyEvent ke = new KeyEvent(table, KeyEvent.KEY_PRESSED,
          //                                   EventQueue.getMostRecentEventTime(),
          //                                   0, KeyEvent.VK_F2, KeyEvent.CHAR_UNDEFINED);
          KeyEvent ke = new KeyEvent(JGroupableTable.this, KeyEvent.KEY_PRESSED,
            EventQueue.getMostRecentEventTime(), 0, KeyEvent.VK_UNDEFINED,
            (char) KeyEvent.VK_F2);
          processKeyEvent(ke);
        }
      });
    }

    //进入编辑状态
    //editCellAt(rowIndex, columnIndex);
  }

  @Override
  public boolean editCellAt(int row, int column, EventObject e) {
    if (this.isReadOnly())
      return false;
    boolean retValue = super.editCellAt(row, column, e);

    Component editComp = getEditorComponent();
    if (editComp != null) {
      boolean added = false;
      for (KeyListener l : editComp.getKeyListeners()) {
        if (l == JGroupableTable.enter2Table) {
          added = true;
          break;
        }
      }
      if (!added)
        editComp.addKeyListener(enter2Table);

      Component c = getEditorComponent();
      if (c != null) {
        c.requestFocus();
        if (c instanceof JTextField) {
          JTextField jf = (JTextField) getEditorComponent();
          jf.selectAll();
        } else if (c instanceof EllipsisEditorPanel) {
          EllipsisEditorPanel editorPanel = (EllipsisEditorPanel) c;
          final JTextField textComponent = editorPanel.getTextComponent();
          textComponent.requestFocus();
          textComponent.selectAll();
        }
      }
    }
    return retValue;
  }

  @Override
  public void addNotify() {
    super.addNotify();
  }

  @Override
  public void removeNotify() {
    super.removeNotify();
  }

  public JGroupableTable() {
    super();
    init();
  }

  /**
   * 执行内部初始化操作
   * @see #initColors()
   */
  private void init() {
    setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
    addPropertyChangeListener("model", new PropertyChangeListener() {
      @Override
      public void propertyChange(PropertyChangeEvent evt) {
        if (evt.getNewValue() == null)
          return;
        //        dataModel.addTableModelListener(new TableModelListener() {
        //          public void tableChanged(TableModelEvent e) {
        //            getTableRowHeader().getTableRowHeaderListModel().fireContentsChanged(
        //              getTableRowHeader(), e.getFirstRow(), e.getLastRow());
        //            loadPreferences();
        //            installTableRowHeader();
        //          }
        //        });
        loadPreferences();
        installTableRowHeader();
      }
    });
    getTableRowHeader().getRowHeaderUpperLeftComponent().addMouseListener(
      new MouseAdapter() {
        @Override
        public void mouseClicked(MouseEvent e) {
          if (SwingUtilities.isLeftMouseButton(e)) {
            if (e.getClickCount() == 2) {
              fitColumnsWidth();
            }
          } else if (SwingUtilities.isRightMouseButton(e)) {
            if (e.getClickCount() == 1) {
              showPopupMenu((Component) e.getSource(), e.getX(), e.getY());
            }
          }
        }
      });
    addKeyListener(enter2Table);

    popupMenu.addPopupMenuListener(new PopupMenuListener() {

      @Override
      public void popupMenuCanceled(PopupMenuEvent e) {
      }

      @Override
      public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
      }

      @Override
      public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
        updatePopupMenuComponentEnable();
      }
    });
  }

  @Override
  protected TableColumnModel createDefaultColumnModel() {
    HideableTableColumnModel model = new HideableTableColumnModel();
    return model;
  }

  @Override
  public void createDefaultColumnsFromModel() {
    TableModel m = getModel();
    if (m != null) {
      // Remove any current columns
      TableColumnModel cm = getColumnModel();
      while (cm.getColumnCount() > 0) {
        cm.removeColumn(cm.getColumn(0));
      }

      // Create new columns from the data model info
      for (int i = 0; i < m.getColumnCount(); i++) {
        TableColumn newColumn = new TableColumn(i);
        addColumn(newColumn);
        newColumn.setIdentifier(newColumn.getHeaderValue());
      }
    }
    updatePopupMenu();
  }

  /**
   * 显示右键菜单
   * @param invoker
   * @param x
   * @param y
   * @see JGroupableTable#popupMenu
   * @see #updatePopupMenu()
   * @see #updatePopupMenuComponentEnable()
   */
  protected void showPopupMenu(Component invoker, int x, int y) {
    if (!isShowTableHeaderPopupMenu()) {
      return;
    }
    updatePopupMenuComponentEnable();
    popupMenu.show(invoker, x, y);
  }

  /**
   * 更新导出菜单的启用/禁用状态.
   * @see #popupMenu
   * @see #updatePopupMenu()
   */
  protected void updatePopupMenuComponentEnable() {
    //已选 择字段的CheckBox
    ArrayList<JCheckBox> selectedCheckBoxs = new ArrayList<JCheckBox>(); //已经选择的列
    //将所有的已经选择的Checkbox添加到一个List
    for (int i = 0; i < popupMenu.getComponentCount(); i++) {
      Component component = popupMenu.getComponent(i);
      if (component instanceof JCheckBox) {
        JCheckBox checkBox = (JCheckBox) component;
        checkBox.setEnabled(true);
        checkBox.setToolTipText(null);
        if (checkBox.isSelected()) {
          selectedCheckBoxs.add(checkBox);
        }
      }
    }
    //如果只选择一个列，则禁用之
    if (selectedCheckBoxs.size() == 1) {
      JCheckBox checkBox = selectedCheckBoxs.get(0);
      checkBox.setEnabled(false);
      checkBox.setToolTipText("最后一列不能被隐 藏");
    }
  }

  @Override
  protected JTableHeader createDefaultTableHeader() {
    return new JGroupableTableHeader(columnModel);
  }

  @Override
  public JGroupableTableHeader getTableHeader() {
    //检查表头是否正确
    JTableHeader result = super.getTableHeader();
    if (result == null || !(result instanceof JGroupableTableHeader)) {
      result = new JGroupableTableHeader(columnModel);
      setTableHeader(result);
    }
    return (JGroupableTableHeader) result;
  }

  public void export2Excel(String fileName, JProgressBar progressBar)
    throws Exception {
    export2Excel(fileName, progressBar, new DefaultRowFilter(),
      new DefaultColumnFilter());
  }

  public void export2Excel(String fileName, JProgressBar progressBar,
    RowFilter rowFilter) throws Exception {
    this.export2Excel(fileName, progressBar, rowFilter, new DefaultColumnFilter());
  }

  /**
   * 将数据导出到Excel中.
   * @param fileName 必须为有效的文件名(不要求文件存在，但路径必须合法)
   * @param progressBar - 可以为null
   * @throws Exception
   * @see #exportTableHeaderComponent(WritableSheet, Row, JGroupableTableHeader, ColumnFilter)
   * @see #exportTableHeader(JProgressBar, WritableSheet, Row, JGroupableTableHeader, ColumnFilter)
   * @see #exportTableData(JProgressBar, RowFilter, WritableSheet, Row, ColumnFilter)
   * @see #exportTableTotalLineData(JProgressBar, RowFilter, WritableSheet, Row, ColumnFilter)
   * 
   */
  public void export2Excel(String fileName, JProgressBar progressBar,
    RowFilter rowFilter, ColumnFilter columnFilter) throws Exception {
    if (fileName == null) {
      throw new IllegalArgumentException("fileName can't be null");
    }
    if (rowFilter == null)
      rowFilter = new DefaultRowFilter();
    WorkbookSettings ws = new WorkbookSettings();
    ws.setLocale(Locale.getDefault());
    WritableWorkbook workbook = Workbook.createWorkbook(new File(fileName), ws);
    WritableSheet writableSheet = workbook.createSheet("DATA", 0);
    if (getSheetOrientation() == SheetOrientation.HORIZONTAL) {
      writableSheet.setPageSetup(PageOrientation.LANDSCAPE);
    } else {
      writableSheet.setPageSetup(PageOrientation.PORTRAIT);
    }

    //配置进度条
    if (progressBar != null) {
      progressBar.setMaximum((getRowCount() + 1) * getColumnCount());
      progressBar.setMinimum(0);
      progressBar.setValue(0);
    }

    Row row = new Row(0); //Excel当前行指针

    JGroupableTableHeader header = getTableHeader();
    exportTableHeaderComponent(writableSheet, row, header, columnFilter);
    exportTableHeader(progressBar, writableSheet, row, header, columnFilter);
    exportTableData(progressBar, rowFilter, writableSheet, row, columnFilter);
    exportTableTotalLineData(progressBar, rowFilter, writableSheet, row,
      columnFilter);

    for (int i = 0, cellColumnIndex = 0; i < getColumnCount(); i++) {
      if (!columnFilter.accept(i, this)) {
        continue;
      }
      int columnWidth = getColumnModel().getColumn(i).getWidth();
      CellView cellView = new CellView();
      cellView.setSize(columnWidth * 48); //注48是试出来的，没什么根据
      writableSheet.setColumnView(cellColumnIndex++, cellView);
    }

    workbook.write();
    workbook.close();
    if (progressBar != null) {
      progressBar.setValue(progressBar.getMaximum());
    }
  }

  /**
   * 导出表数据
   * @param progressBar
   * @param rowFilter
   * @param writableSheet
   * @param row
   * @throws WriteException
   * @throws RowsExceededException
   * @see #export2Excel(String, JProgressBar)
   */
  protected void exportTableData(JProgressBar progressBar, RowFilter rowFilter,
    WritableSheet writableSheet, Row row, ColumnFilter columnFilter)
    throws WriteException, RowsExceededException {
    if (columnFilter == null) {
      columnFilter = new DefaultColumnFilter();
    }
    for (int rowIndex = 0; rowIndex < getRowCount(); rowIndex++) {
      if (!rowFilter.accept(rowIndex, this)) {
        continue;
      }
      for (int colIndex = 0, cellColumnIndex = 0; colIndex < getColumnCount(); colIndex++) {
        if (progressBar != null) {
          progressBar.setValue((rowIndex + 2) * getColumnCount() + colIndex);
        }
        if (!columnFilter.accept(colIndex, this)) {
          continue;
        }
        Object value = getDisplayValueAt(rowIndex, colIndex);
        final WritableCell writableCell = getWritableCellAt(rowIndex, colIndex,
          value);
        writableCell.setCellFormat(getCellFormatPrivoder().getDataCellFormatAt(
          rowIndex, colIndex, this));
        writableSheet.addCell(writableCell.copyTo(cellColumnIndex, row.getIndex()));
        cellColumnIndex++;
      }
      row.inc();
    }
  }

  /**
   * @param progressBar
   * @param rowFilter
   * @param writableSheet
   * @param row
   * @throws WriteException
   * @throws RowsExceededException
   * @see #export2Excel(String, JProgressBar)
   */
  protected void exportTableTotalLineData(JProgressBar progressBar,
    RowFilter rowFilter, WritableSheet writableSheet, Row row,
    ColumnFilter columnFilter) throws WriteException, RowsExceededException {
    if (totalLineModel == null) {
      return;
    }
    int rowIndex = row.getIndex();
    for (int colIndex = 0, cellColumnIndex = 0; colIndex < getColumnCount(); colIndex++) {
      if (!columnFilter.accept(colIndex, this)) {
        continue;
      }
      Object value = getTotalLineModel().getTotalValuteAt(
        convertColumnIndexToModel(colIndex));
      final WritableCell writableCell = getWritableCellAt(rowIndex, colIndex, value);
      writableCell.setCellFormat(getCellFormatPrivoder().getTotalCellFormatAt(
        colIndex, this));
      writableSheet.addCell(writableCell.copyTo(cellColumnIndex, row.getIndex()));
      cellColumnIndex++;
    }
    row.inc();
  }

  /**
   * 导出表头，不包含HeaderComponent.
   * @param progressBar
   * @param writableSheet
   * @param row
   * @param header
   * @throws WriteException
   * @throws RowsExceededException
   * @see #export2Excel(String, JProgressBar)
   */
  protected void exportTableHeader(JProgressBar progressBar,
    WritableSheet writableSheet, Row row, JGroupableTableHeader header,
    ColumnFilter columnFilter) throws WriteException, RowsExceededException {
    if (columnFilter == null) {
      columnFilter = new DefaultColumnFilter();
    }
    if (header != null) {
      WritableCellFormat headerCellFormatAt = getCellFormatPrivoder()
        .getHeaderCellFormatAt(this);
      if (header.getColumnGroupCount() > 0) {
        for (ColumnGroup group : header.getColumnGroups()) {
          Object title = group.getHeaderValue();
          int fromIndex = -1; //开始合并的列索引号
          for (int i = 0, cellColumnIndex = 0; i < columnModel.getColumnCount(); i++) {

            if (!columnFilter.accept(i, this)) {
              continue;
            }
            TableColumn column = columnModel.getColumn(i);
            if (group.isInclude(column)) {
              if (fromIndex == -1) {
                fromIndex = cellColumnIndex;
              }
              //如果当前组不包含下一列，则进行单元格合并
              if (i == columnModel.getColumnCount() - 1
                || !group.isInclude(columnModel.getColumn(i + 1))) {

                Label label = new Label(fromIndex, row.getIndex(),
                  title == null ? "" : title.toString(), headerCellFormatAt);
                writableSheet.addCell(label);
                writableSheet.mergeCells(fromIndex, row.getIndex(), cellColumnIndex,
                  row.getIndex());
                fromIndex = -1;
              }
            }
            cellColumnIndex++;
          }
        }
        row.inc(); //下移一行
      }

      for (int i = 0, cellColumnIndex = 0; i < header.getColumnModel()
        .getColumnCount(); i++) {
        if (!columnFilter.accept(i, this)) {
          continue;
        }

        if (progressBar != null) {
          progressBar.setValue(1 * i);
        }
        //writableSheet.setColumnView(i, getTableHeader().getColumnModel().
        //getColumn(i).getWidth()); //设置列宽度
        TableColumn column = header.getColumnModel().getColumn(i);
        String title = (String) column.getHeaderValue();
        String displayLabel = title;
        if (displayLabel == null) {
          displayLabel = title;
        }

        //如果未被任何级包含，则进行分组上下合并
        if (header.getColumnGroupCount() > 0
          && header.getColumnGroup(column) == null) {
          Label l = new Label(cellColumnIndex, row.getIndex() - 1, displayLabel,
            headerCellFormatAt);
          writableSheet.addCell(l);
          writableSheet.mergeCells(cellColumnIndex, row.getIndex() - 1,
            cellColumnIndex, row.getIndex());
        } else {
          Label l = new Label(cellColumnIndex, row.getIndex(), displayLabel,
            headerCellFormatAt);
          writableSheet.addCell(l);
        }
        cellColumnIndex++;
      }
      row.inc();
    }
  }

  /**
   * 返回需要导出的列数
   * @param columnFilter
   * @return
   */
  private int getExportColumnCount(ColumnFilter columnFilter) {
    int retVal = 0;
    for (int i = 0; i < getColumnCount(); i++) {
      if (columnFilter.accept(i, this)) {
        retVal++;
      }
    }
    return retVal;
  }

  /**
   * 导出HeaderComponent
   * @param writableSheet
   * @param row
   * @param header
   * @param columnFilter
   * @throws WriteException
   * @throws RowsExceededException
   * @see #export2Excel(String, JProgressBar, RowFilter, ColumnFilter)
   */
  protected void exportTableHeaderComponent(WritableSheet writableSheet, Row row,
    JGroupableTableHeader header, ColumnFilter columnFilter) throws WriteException,
    RowsExceededException {
    if (columnFilter == null) {
      columnFilter = new DefaultColumnFilter();
    }
    if (header.getHeaderComponent() != null) {
      HeaderComponent c = header.getHeaderComponent();
      if (c.getRowElements() != null) {
        for (RowElement element : c.getRowElements()) {
          if (element == null) {
            continue;
          }
          Font font = element.getFont();
          WritableFont wFont = new WritableFont(WritableFont.createFont(font
            .getFamily()));
          wFont.setPointSize(font.getSize());
          switch (font.getStyle()) {
          case Font.BOLD:
            wFont.setBoldStyle(WritableFont.BOLD);
            break;
          case Font.ITALIC:
            wFont.setItalic(true);
            break;
          default:
            break;
          }
          WritableCellFormat format = new WritableCellFormat(wFont);
          Alignment ali;
          switch (element.getHorizontalAlignment()) {
          case SwingConstants.LEFT:
            ali = Alignment.LEFT;
            break;
          case SwingConstants.CENTER:
            ali = Alignment.CENTRE;
            break;
          case SwingConstants.RIGHT:
            ali = Alignment.RIGHT;
            break;
          default:
            ali = Alignment.LEFT;
            break;
          }
          format.setAlignment(ali);
          Label l = new Label(0, row.getIndex(), element.getValue(), format);

          writableSheet.addCell(l);
          writableSheet.mergeCells(0, row.getIndex(),
            getExportColumnCount(columnFilter) - 1, row.getIndex());
          row.inc();
        }
      }
    }
  }

  /**
   * 
   * @param row
   * @param column
   * @return 根据row和column对应的显示的值.
   */
  public Object getDisplayValueAt(int row, int column) {
    TableCellRenderer cellRenderer = getCellRenderer(row, column);
    Object value = super.getValueAt(row, column);
    return ((TableCellDisplayValueProvider) cellRenderer).getDisplayValue(this,
      value, row, column);
  }

  /**
   * 
   * @param col
   * @param row
   * @param value
   * @return 导出到Excel时对应列、行的WritableCell实例对象，子类通过覆盖本方法返回具体的对象实例，默认返回.<code>jxl.write.Label</code>
   * @see jxl.write.Label
   * @see jxl.write.Number
   * @see jxl.write.Blank
   * @see jxl.write.Boolean
   * @see jxl.write.DateTime
   * @see jxl.write.Formula
   * @see jxl.write.Label
   */
  protected WritableCell getWritableCellAt(int row, int col, Object value) {
    int index = convertColumnIndexToModel(col);
    Class clazz = getModel().getColumnClass(index);
    if ((Number.class.isAssignableFrom(clazz)) || clazz == int.class
      || clazz == double.class || clazz == float.class || clazz == long.class) {
      BigDecimal bigDecimal = new BigDecimal(0);
      try {
        bigDecimal = new BigDecimal(value == null ? "0" : value.toString());
      } catch (Throwable e) {
        System.err.println(clazz.getName());
        System.err.println(value);
        e.printStackTrace();
      }
      return new jxl.write.Number(col, row, bigDecimal.doubleValue());
    }
    if (value instanceof Date) {
      value = new SimpleDateFormat("yyyy-MM-dd").format(value);
    }
    return new Label(col, row, value == null ? "" : value.toString());
  }

  @Override
  public TableCellRenderer getCellRenderer(final int _row, final int _column) {
    TableCellRenderer renderer = super.getCellRenderer(_row, _column);
    if (!(renderer instanceof AppearanceDectoratorCellRenderer)) {
      renderer = new AppearanceDectoratorCellRenderer(renderer);
      getColumnModel().getColumn(_column).setCellRenderer(renderer);
    }
    return renderer;
  }

  /**
   * @param row
   * @param column
   * @return 指定行、列坐标的背景色
   * @see #getCellForeground(int, int)
   */
  protected Color getCellBackground(int row, int column) {
    if (row % 2 != 0) {
      return getAlternatingColor();
    } else {
      return getBackground();
    }
  }

  @Override
  public void updateUI() {
    if (UIManager.get(uiClassID) == null) {
      //      UIManager.setLookAndFeel(new com.sun.java.swing.plaf.motif.MotifLookAndFeel());
      UIManager.getLookAndFeelDefaults().put(uiClassID,
        "com.ufgov.smartclient.plaf.basic.BasicGroupableTableUI");
    }
    super.updateUI();
    getTableHeader().updateUI();
    getTableRowHeader().updateUI();
    class TableTransferHandler extends TransferHandler implements UIResource {

      /**
       * Create a Transferable to use as the source for a data transfer.
       *
       * @param c  The component holding the data to be transfered.  This
       *  argument is provided to enable sharing of TransferHandlers by
       *  multiple components.
       * @return  The representation of the data to be transfered.
       *
       */
      @Override
      protected Transferable createTransferable(JComponent c) {
        if (c instanceof JTable) {
          JTable table = (JTable) c;
          int[] rows;
          int[] cols;

          if (!table.getRowSelectionAllowed() && !table.getColumnSelectionAllowed()) {
            return null;
          }

          if (!table.getRowSelectionAllowed()) {
            int rowCount = table.getRowCount();

            rows = new int[rowCount];
            for (int counter = 0; counter < rowCount; counter++) {
              rows[counter] = counter;
            }
          } else {
            rows = table.getSelectedRows();
          }

          if (!table.getColumnSelectionAllowed()) {
            int colCount = table.getColumnCount();

            cols = new int[colCount];
            for (int counter = 0; counter < colCount; counter++) {
              cols[counter] = counter;
            }
          } else {
            cols = table.getSelectedColumns();
          }

          if (rows == null || cols == null || rows.length == 0 || cols.length == 0) {
            return null;
          }

          StringBuffer plainBuf = new StringBuffer();
          StringBuffer htmlBuf = new StringBuffer();

          htmlBuf.append("<html>\n<body>\n<table>\n");

          for (int row = 0; row < rows.length; row++) {
            htmlBuf.append("<tr>\n");
            for (int col = 0; col < cols.length; col++) {
              Object obj = getDisplayValueAt(rows[row], cols[col]);
              String val = ((obj == null) ? "" : obj.toString());
              plainBuf.append(val + "\t");
              htmlBuf.append("  <td>" + val + "</td>\n");
            }
            // we want a newline at the end of each line and not a tab
            plainBuf.deleteCharAt(plainBuf.length() - 1).append("\n");
            htmlBuf.append("</tr>\n");
          }

          // remove the last newline
          plainBuf.deleteCharAt(plainBuf.length() - 1);
          htmlBuf.append("</table>\n</body>\n</html>");

          return new BasicTransferable(plainBuf.toString(), htmlBuf.toString());
        }

        return null;
      }

      @Override
      public int getSourceActions(JComponent c) {
        return COPY;
      }

    }

    setTransferHandler(new TableTransferHandler());
    if (tableRowHeader != null)
      tableRowHeader.updateUI();
  }

  /**
   * @param row
   * @param column
   * @return 指定行、列坐标的前景色
   * @see #getCellBackground(int, int)
   */
  protected Color getCellForeground(int row, int column) {
    return getForeground();
  }

  @Override
  public Printable getPrintable(PrintMode printMode, MessageFormat headerFormat,
    MessageFormat footerFormat) {
    return new TablePrintable(this, printMode, headerFormat, footerFormat);
  }

  /**
   * 保存首选参数
   * @see #loadPreferences()
   */
  public synchronized void savePreferences() {
    String name = getPreferencesKey();
    if (name == null)
      return;
    final Map<Object, Object> map = new HashMap<Object, Object>();
    saveState(map);

    new Thread() {
      @Override
      public void run() {
        try {
          getPreferenceStore().save(getPreferencesKey(), object2ByteArray(map));
          map.clear();
        } catch (IOException e) {
          System.err.println("配置保存失败：" + e.getMessage());
        }
      };
    }.start();
  }

  /**
   * 加载首选参数
   * @see #savePreferences()
   */
  public synchronized void loadPreferences() {
    String name = getPreferencesKey();
    if (name == null)
      return;
    byte[] data = getPreferenceStore().load(name);
    if (data == null)
      return;
    Map<Object, Object> map;
    try {
      map = (Map<Object, Object>) byteArray2Object(data);
    } catch (Exception e1) {
      System.err.println("加载失败：" + e1.getMessage());
      return;
    }
    restoreState(map);
    map.clear();
  }

  /**
   * 把状态保存到map中，子类可覆盖该方法，保存自定义状态
   * @param map 保存修改的map对象，注意：map中放置的对象需要实现 {@link Serializable}或 {@link Externalizable}
   * @see #restoreState(Map)
   */
  protected synchronized void saveState(Map<Object, Object> map) {
    if (columnModel instanceof HideableTableColumnModel) {
      HideableTableColumnModel cModel = (HideableTableColumnModel) columnModel;
      Set<Object> orderSet = new LinkedHashSet<Object>();
      Map<Object, Object> widthMap = new LinkedHashMap<Object, Object>();

      for (TableColumn c : cModel.getAllTableColumns()) {
        Object identifier = c.getIdentifier();
        orderSet.add(identifier);
        widthMap.put(identifier, c.getPreferredWidth());
      }

      Set<Object> hidedSet = new LinkedHashSet<Object>();
      for (TableColumn c : cModel.getHidedTableColumns()) {
        Object identifier = c.getIdentifier();
        hidedSet.add(identifier);
      }
      debug(orderSet);
      map.put("width_map", widthMap);
      map.put("order_set", orderSet);
      map.put("hided_set", hidedSet);
    }

    //保存位置
    if (getParent() instanceof JViewport) {
      map.put("VIEW_LOCATION", ((JViewport) getParent()).getViewPosition());
    }
  }

  /**
   * 恢复状态，放到map中的对象必须实现 {@link Serializable}或 {@link Externalizable}
   * @param map 保存状态信息的map
   * @see #saveState(Map)
   */
  protected synchronized void restoreState(Map<Object, Object> map) {
    //属性对象
    Map<Object, Object> widthMap = (Map<Object, Object>) map.get("width_map");
    Set<Object> orderSet = (Set<Object>) map.get("order_set");
    Set<Object> hidedSet = (Set<Object>) map.get("hided_set");

    //有效性检查
    widthMap = widthMap == null ? new LinkedHashMap<Object, Object>() : widthMap;
    orderSet = orderSet == null ? new LinkedHashSet<Object>() : orderSet;
    hidedSet = hidedSet == null ? new LinkedHashSet<Object>() : hidedSet;

    //恢复列宽度
    Map<Object, TableColumn> columnMap = new LinkedHashMap<Object, TableColumn>();
    for (Enumeration<TableColumn> e = columnModel.getColumns(); e.hasMoreElements();) {
      TableColumn column = e.nextElement();

      Object identifier = column.getIdentifier();
      if (widthMap.containsKey(identifier)) {
        column.setPreferredWidth((Integer) widthMap.get(identifier));
      }
      columnMap.put(identifier, column);
    }

    //恢复列顺序
    if (orderSet != null) {
      Set<TableColumn> columnSet = new LinkedHashSet<TableColumn>(); //保存添加到columnModel中的列
      //新建一个model，按顺序添加进来
      TableColumnModel oldTableColumnModel = getColumnModel();
      TableColumnModel cModel = createDefaultColumnModel();
      setColumnModel(cModel);
      for (Object identifier : orderSet) {
        TableColumn column = columnMap.get(identifier);
        if (column != null) {
          columnSet.add(column);
          addColumn(column);
        }
      }
      /*
       * 在有些情况下，新的model中添加了列，但不在保存的顺序中，这种情况下会出现
       * 新列不被添加到columnModel中的情况，从而不显示这种列，下面这段code将处理
       * 这种情况带来的问题，新列一率添加进来
       */
      for (int i = 0; i < oldTableColumnModel.getColumnCount(); i++) {
        TableColumn column = oldTableColumnModel.getColumn(i);
        if (column != null && !columnSet.contains(column)) {
          addColumn(column);
        }
      }
      updatePopupMenu(); //刷新菜单顺序
    }

    //隐藏列
    if (hidedSet != null && (hidedSet.size() < getColumnCount())) {
      for (Object identifier : hidedSet) {
        TableColumn column = columnMap.get(identifier);
        if (column != null) {
          hideColumn(column);
        }
      }
    }

    //恢复位置
    if (getParent() instanceof JViewport) {
      Point viewLocation = (Point) map.get("VIEW_LOCATION");
      if (viewLocation != null)
        ((JViewport) getParent()).setViewPosition(viewLocation);
    }
  }

  /**
   * 隐藏指定的列
   * @param column
   * @see JGroupableTable#showColumns(Object[])
   */
  private synchronized void hideColumn(TableColumn column) {
    for (JCheckBox checkBox : checkBoxColumnMap.keySet()) {
      if (checkBoxColumnMap.get(checkBox) == column) {
        checkBox.setSelected(false);
        columnModel.removeColumn(column);
      }
    }
  }

  /**
   * 转换object为byte[]数组
   * @param object Object
   * @return byte[]
   * @see #byteArray2Object(byte[])
   */
  private byte[] object2ByteArray(Object object) throws IOException {
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    ObjectOutputStream oos = new ObjectOutputStream(bos);
    oos.writeObject(object);
    oos.flush();
    byte[] retValue = bos.toByteArray();
    bos.close();
    return retValue;
  }

  /**
   * 转换bytes为Object，注意，该方法只读取第一个Object，一般和 {@link #byteArray2Object(byte[])} 配套使用
   * @param bytes byte[] object的字节表示形式数组
   * @return 由byte[]转换成的一个对象
   * @throws IOException
   * @throws ClassNotFoundException
   * @see #object2ByteArray(Object)
   */
  private Object byteArray2Object(byte[] bytes) throws IOException,
    ClassNotFoundException {
    ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
    ObjectInputStream ois = new ObjectInputStream(bis);
    Object retValue = ois.readObject();
    ois.close();
    return retValue;
  }

  /**
   * 安装行表头组件
   * @see #createDefaultTableHeader()
   * @see #getTableRowHeader()
   */
  protected void installTableRowHeader() {
    if (ownerScrollPane != null) {
      TableRowHeader tableRowHeader = getTableRowHeader();
      if (ownerScrollPane.getRowHeader().getView() != tableRowHeader) {
        //ownerScrollPane.setRowHeaderView(null);
        ownerScrollPane.setRowHeaderView(tableRowHeader);
      }
      if (ownerScrollPane.getCorner(ScrollPaneConstants.UPPER_LEFT_CORNER) != getTableRowHeader()
        .getRowHeaderUpperLeftComponent()) {
        ownerScrollPane.setCorner(ScrollPaneConstants.UPPER_LEFT_CORNER,
          getTableRowHeader().getRowHeaderUpperLeftComponent());
      }
    }
  }

  /**
   * @return 查找到的最顶导组件
   */
  protected Component findAncestor() {
    Component comp = this;
    while (comp != null && comp.getParent() != null) {
      comp = comp.getParent();
    }
    return comp;
  }

  @Override
  public boolean print(PrintMode printMode, MessageFormat headerFormat,
    MessageFormat footerFormat, boolean showPrintDialog,
    PrintRequestAttributeSet attr, boolean interactive, PrintService service)
    throws PrinterException, HeadlessException {

    //  complain early if an invalid parameter is specified for headless mode
    boolean isHeadless = GraphicsEnvironment.isHeadless();
    if (isHeadless) {
      if (showPrintDialog) {
        throw new HeadlessException("Can't show print dialog.");
      }

      if (interactive) {
        throw new HeadlessException("Can't run interactively.");
      }
    }

    //  Get a PrinterJob.
    //  Do this before anything with side-effects since it may throw a
    //  security exception - in which case we don't want to do anything else.
    final PrinterJob job = com.ufgov.smartclient.component.table.PrinterJob
      .getInstance();

    if (isEditing()) {
      //    try to stop cell editing, and failing that, cancel it
      if (!getCellEditor().stopCellEditing()) {
        getCellEditor().cancelCellEditing();
      }
    }

    if (attr == null) {
      attr = new HashPrintRequestAttributeSet();
    }

    final PrintingStatus printingStatus;

    //  fetch the Printable 
    Printable printable = getPrintable(printMode, headerFormat, footerFormat);

    if (interactive) {
      //    wrap the Printable so that we can print on another thread
      printingStatus = PrintingStatus.createPrintingStatus(this, job);
      printable = printingStatus.createNotificationPrintable(printable);
    } else {
      //    to please compiler
      printingStatus = null;
    }

    //  set the printable on the PrinterJob 
    job.setPrintable(printable);

    //  if specified, set the PrintService on the PrinterJob
    if (service != null) {
      job.setPrintService(service);
    }

    //  if requested, show the print dialog
    if (showPrintDialog && !job.printDialog(attr)) {
      //    the user cancelled the print dialog
      return false;
    }

    //  if not interactive, just print on this thread (no dialog)
    if (!interactive) {
      //    do the printing
      job.print(attr);

      //    we're done
      return true;
    }

    final PrintRequestAttributeSet copyAttr = attr;

    //  this runnable will be used to do the printing
    //  (and save any throwables) on another thread
    Runnable runnable = new Runnable() {
      public void run() {
        try {
          //        do the printing
          job.print(copyAttr);
        } catch (Throwable t) {
          //        save any Throwable to be rethrown

        } finally {
          //        we're finished - hide the dialog
          printingStatus.dispose();
        }
      }
    };

    //  start printing on another thread
    Thread th = new Thread(runnable);
    th.start();

    printingStatus.showModal(true);
    return true;
  }

  /**
   * @return 表格是否为只读，true，只读，false，可以编辑
   * @see #setReadOnly(boolean)
   */
  public boolean isReadOnly() {
    return readOnly;
  }

  /**
   * 设置表格是否只读
   * @param readOnly true，只读，false，可以编辑
   * @see JGroupableTable#isReadOnly()
   */
  public void setReadOnly(boolean readOnly) {
    this.readOnly = readOnly;
  }

  /**
   * @return 表格是否即时编辑(true/false)
   * @see #setInstantEdit(boolean)
   */
  public boolean isInstantEdit() {
    return instantEdit;
  }

  /**
   * 设置表格是否即时编辑
   * @param instantEdit
   * @see #isInstantEdit()
   */
  public void setInstantEdit(boolean instantEdit) {
    this.instantEdit = instantEdit;
  }

  @Override
  public void print(Graphics g) {
    super.print(g);
    g.setColor(getGridColor());
    g.drawLine(0, 0, 0, getHeight());
  }

  /**
   * 刷新/更新右键菜单
   * @see #updatePopupMenuComponentEnable()
   * @see #popupMenu
   */
  protected void updatePopupMenu() {
    if (checkBoxColumnMap == null) {
      checkBoxColumnMap = new HashMap<JCheckBox, TableColumn>();
    } else {
      checkBoxColumnMap.clear();
    }

    if (popupMenu == null) {
      popupMenu = createDefaultPopupMenu();
    } else {
      popupMenu.removeAll();
    }

    TableColumnModel columnModel = getColumnModel();
    for (int i = 0; i < columnModel.getColumnCount(); i++) {

      final TableColumn column = columnModel.getColumn(i);
      final JCheckBox checkBox = new JCheckBox(column.getHeaderValue().toString());
      //在column headerValue变化时更新CheckBox标题
      column.addPropertyChangeListener(new PropertyChangeListener() {

        @Override
        public void propertyChange(PropertyChangeEvent evt) {
          if (evt.getPropertyName().equals("headerValue")) {
            checkBox.setText((String) evt.getNewValue());
          }
        }
      });
      checkBoxColumnMap.put(checkBox, column);
      checkBox.setFocusable(false);
      checkBox.setSelected(true);
      checkBox.setOpaque(false);
      checkBox.addActionListener(new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent e) {
          JCheckBox box = (JCheckBox) e.getSource();
          TableColumn aColumn = checkBoxColumnMap.get(box);
          if (box.isSelected()) {
            addColumn(aColumn);
          } else {
            removeColumn(aColumn);
          }
          updatePopupMenuComponentEnable();
          savePreferences();
        }
      });
      popupMenu.add(checkBox);
    }

    popupMenu.addSeparator();
    popupMenu.add(new AbstractAction("全部显示") {
      @Override
      public void actionPerformed(ActionEvent e) {
        for (JCheckBox checkBox : checkBoxColumnMap.keySet()) {
          if (!checkBox.isSelected()) {
            getColumnModel().addColumn(checkBoxColumnMap.get(checkBox));
            checkBox.setSelected(true);
          }
        }
        savePreferences();
      }
    });
  }

  /**
   * @return 默认的PopupMenu，子类可覆盖修改
   * @see #updatePopupMenu()
   * @see #updatePopupMenuComponentEnable()
   */
  protected JPopupMenu createDefaultPopupMenu() {
    return new JPopupMenu() {

      @Override
      public void setLayout(LayoutManager mgr) {
        super.setLayout(new ScrollableMenuLayout());
      }
    };
  }

  /**
   * 隐藏指定的列，注：如果column的HeaderValue与identifiers不同，请使用identifiers
   * @param identifiers
   * @see #showColumns(Object[])
   */
  public synchronized void hideColumns(Object[] identifiers) {
    for (Object id : identifiers) {
      boolean colExists = false; //列是否存在
      for (JCheckBox checkBox : checkBoxColumnMap.keySet()) {
        colExists = true;
        TableColumn col = checkBoxColumnMap.get(checkBox);
        if (col.getIdentifier().equals(id)) {
          if (checkBox.isSelected()) {
            checkBox.setSelected(false);
            removeColumn(col);
            break;
          }
        }
      }
      if (!colExists) {
        throw new IllegalArgumentException(String.format("列 %s 不存在", id));
      }
    }
  }

  /**
   * 显示指定的列，注：如果column的HeaderValue与identifiers不同，请使用identifiers
   * @param identifiers
   * @see JGroupableTable#hideColumn(TableColumn)
   * @see #hideColumns(Object[])
   */
  public synchronized void showColumns(Object[] identifiers) {
    for (Object id : identifiers) {
      boolean colExists = false; //列是否存在
      for (JCheckBox checkBox : checkBoxColumnMap.keySet()) {
        TableColumn col = checkBoxColumnMap.get(checkBox);
        Object identifier = col.getIdentifier();
        if (identifier.equals(id)) {
          colExists = true;
          if (!checkBox.isSelected()) {
            checkBox.setSelected(true);
            addColumn(col);
            break;
          }
        }
      }
      if (!colExists) {
        throw new IllegalArgumentException(String.format("列 %s 不存在", id));
      }
    }
  }

  /**
   * @return 被勾选的行
   * @see #setCheckedRows(Integer[], boolean)
   */
  public Integer[] getCheckedRows() {
    return tableRowHeader.getCheckedRows();
  }

  /**
   * 选定指定的行
   * @param rows 要钩选的行
   * @param notify 是否通知监听器
   * @see #getCheckedRows()
   */
  public void setCheckedRows(Integer[] rows, boolean notify) {
    if (rows == null) {
      throw new IllegalArgumentException("rows 不能为 null");
    }
    for (Integer row : rows) {
      if (row == null || row < 0 || row > getRowCount() - 1) {
        throw new IllegalArgumentException("无效的row " + row);
      }
      tableRowHeader.checkedRow(row, notify);
    }
  }

  /**
   * @return 是否显示CheckBox
   * @see #setShowCheckedColumn(boolean)
   */
  public boolean isShowCheckedColumn() {
    return getTableRowHeader().isShowCheckedColumn();
  }

  @Override
  public boolean isCellEditable(int row, int column) {
    /*当是示统计行时，在单击合计行也会进入编辑状态，主要由于合计行paint在行上面，
     * 下面代码将屏蔽编辑功能
     */

    if (getTotalLineModel() != null) {
      Container parent = getParent();
      if (parent instanceof JViewport) {
        Rectangle viewRect = ((JViewport) parent).getViewRect();
        int y1 = viewRect.y + viewRect.height; //最底下的坐标
        int y2 = y1 - getTotalLineHeight(); //减去合计行的高度
        Rectangle newRect = new Rectangle(0, y2, getWidth(), getHeight()); //计算出统计行的绘制区域
        if (newRect.intersects(getCellRect(row, column, true))) {
          //如果相交说明在统计行区域之内
          return false;
        }
      }
    }
    return super.isCellEditable(row, column);
  }

  /**
   * 设置是否显示Checkbox
   * @param showCheckedColumn
   * @see #isShowCheckedColumn()
   */
  public void setShowCheckedColumn(boolean showCheckedColumn) {
    getTableRowHeader().setShowCheckedColumn(showCheckedColumn);
  }

  @Override
  public void setRowHeight(final int row, final int rowHeight) {
    super.setRowHeight(row, rowHeight);
    getTableRowHeader().updateRowList();
    //    ComponentUtilities.setListRowHeight(getTableRowHeader().getRowList(), row,
    //      rowHeight);
  }

  /**
   * 添加列宽改变时的监听器.
   * @param listener
   */
  public void addColumnResizeListener(ColumnResizeListener listener) {
    listenerList.add(ColumnResizeListener.class, listener);
  }

  /**
   * 删除列宽改变时的监听器
   * @param listener
   */
  public void removeColumnResizeListener(ColumnResizeListener listener) {
    listenerList.remove(ColumnResizeListener.class, listener);
  }

  /**
   * 引发列宽改变事件
   * @param event
   */
  protected void fireColumnResized(ColumnResizeEvent event) {
    ColumnResizeListener[] listeners = listenerList
      .getListeners(ColumnResizeListener.class);
    for (ColumnResizeListener listener : listeners) {
      listener.columnResized(event);
    }
  }

  @Override
  public TableColumn getColumn(Object identifier) {
    if (identifier == null) {
      throw new IllegalArgumentException("identifier can't be null");
    }
    try {
      return super.getColumn(identifier);
    } catch (Exception e) {
    }
    for (TableColumn c : ((HideableTableColumnModel) getColumnModel())
      .getAllTableColumns()) {
      if (identifier.equals(c.getIdentifier())) {
        return c;
      }
    }
    throw new IllegalArgumentException("Identifier not found");
  }

  /**
   * @return 合计行绘制的位置坐标
   */
  public Point getTotalLineLocation() {
    Container parent = getParent();
    if (parent instanceof JViewport) {
      Rectangle viewRect = ((JViewport) parent).getViewRect();
      Dimension preferredSize = getPreferredSize();
      //不存在滚动，即表格大小没有超出ViewPort，直接返回原位置
      if (viewRect.y + viewRect.height >= preferredSize.height) {
        return new Point(0, preferredSize.height - getTotalLineHeight());
      }
      return new Point(0, viewRect.y + viewRect.height - getTotalLineHeight());
    }
    return new Point();
  }

  /**
   * 根据内容自动调整所有列的宽度
   */
  public void fitColumnsWidth() {
    if (getAutoResizeMode() == JTable.AUTO_RESIZE_OFF) {
      JGroupableTableHeader header = getTableHeader();
      for (int i = 0; i < header.getColumnModel().getColumnCount(); i++) {
        header.fitColumnWidth(i, false);
      }
      savePreferences();
    }
  }

  private void debug(Object... args) {
    boolean debug = false;
    if (!debug) {
      return;
    }
    for (Object s : args) {
      System.out.println(s);
    }
  }

  @Override
  public String getUIClassID() {
    return uiClassID;
  }
}